/*
    libnbds
    Non-blocking Data Structures Library

    Copyright (C) 2011 Paweł Dziepak

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "atomic.h"
#include "vector.h"

using namespace nbds;

vector_base::vector_base() {
	buffer *b = new buffer;
	b->data = new void*[1024];
	b->last_idx = 0;
	b->size = 1024;

	for (int i = 0; i < b->size; i++)
		b->data[i] = 0;

	current = b;
}

vector_base::vector_base(unsigned int size) {
	buffer *b = new buffer;
	b->data = new void*[size];
	b->last_idx = 0;
	b->size = size;

	for (int i = 0; i < b->size; i++)
		b->data[i] = 0;

	current = b;
}

vector_base::~vector_base() {
	delete []current->data;
	delete current;
}

void vector_base::push_back_(void *obj) {
	buffer *b = current;

	unsigned int my_idx;

	do {
		b = arch::get(&current);
		my_idx = arch::get(&b->last_idx);
		if (my_idx < b->size) {
			if (arch::cmp_and_swp(b->data + my_idx, 
						(void*)0, obj) == NULL) {
				arch::cmp_and_swp(&b->last_idx, my_idx,
								my_idx + 1);
				break;
			} else
				arch::cmp_and_swp(&b->last_idx, my_idx,
								my_idx + 1);
		} else {
			buffer *nb = new buffer;
			nb->data = new void*[b->size * 2];
			nb->last_idx = my_idx + 1;
			nb->size = b->size * 2;

			for (int i = 0; i < my_idx; i++)
				nb->data[i] = b->data[i];
			nb->data[my_idx] = obj;
			for (int i = my_idx + 1; i < nb->size; i++)
				nb->data[i] = 0;

			if (arch::cmp_and_swp(&current, b, nb) != b) {
				delete []nb->data;
				delete nb;
			} else {
				/* FIXME: b and b->data reclamation */
				break;
			}
		}
	} while (true);
}

void *vector_base::get_(unsigned int idx) {
	buffer *b = arch::get(&current);

	if (arch::get(&b->last_idx) >= idx)
		return arch::get(&b->data[idx]);
	else
		return 0;
}

int vector_base::count_() {
	return current->last_idx;
}

bool vector_base::is_empty_() {
	return !current->last_idx;
}

